import sys
import os
import glob
import subprocess
import json
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, 
                            QHBoxLayout, QLabel, QPushButton, QRadioButton, 
                            QButtonGroup, QFileDialog, QTextEdit, QProgressBar, 
                            QComboBox, QGroupBox, QCheckBox, QMessageBox, QLineEdit,
                            QSplitter, QSizePolicy, QDesktopWidget, QFrame, QScrollArea,
                            QDialog, QListWidget, QListWidgetItem, QInputDialog,
                            QTabWidget, QColorDialog, QPushButton, QSlider)
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QCoreApplication
from PyQt5.QtGui import QIcon, QFont, QPixmap, QPalette, QColor, QFontInfo, QFontMetrics

from config import DESENSE_METHODS, MASK_COLOR, MASK_COLORS_PRESETS, DEFAULT_MOSAIC_SIZE, DEFAULT_REPLACE_CHAR, REPLACE_CHAR_PRESETS
import sys
import io
from utils.logger import logger
# 重新配置标准输出/标准错误为 utf-8
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')


# 导入NER工具模块，读取白名单
import importlib.util
import sys

def import_module_from_path(module_name, file_path):
    """从文件路径导入模块"""
    spec = importlib.util.spec_from_file_location(module_name, file_path)
    if spec is None:
        return None
    module = importlib.util.module_from_spec(spec)
    sys.modules[module_name] = module
    spec.loader.exec_module(module)
    return module

# 导入NER工具模块
ner_utils_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "utils/ner_utils.py")
ner_utils = import_module_from_path("ner_utils", ner_utils_path)

# 资源路径定义
RESOURCES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "resources")
ICONS_DIR = os.path.join(RESOURCES_DIR, "icons")
CONFIG_DIR = os.path.join(RESOURCES_DIR, "config")

# 如果资源目录不存在，创建它们
os.makedirs(RESOURCES_DIR, exist_ok=True)
os.makedirs(ICONS_DIR, exist_ok=True)
os.makedirs(CONFIG_DIR, exist_ok=True)

# 图标和配置文件路径
APP_ICON = os.path.join(ICONS_DIR, "app_icon.png")
WHITELIST_CONFIG = os.path.join(CONFIG_DIR, "whitelist.json")

# 确保白名单配置文件存在
def ensure_whitelist_config():
    """确保白名单配置文件存在，如果不存在则从ner_utils.py导入并创建"""
    if not os.path.exists(WHITELIST_CONFIG):
        try:
            # 导入NER工具模块，读取白名单
            import importlib.util
            import sys
            
            def import_module_from_path(module_name, file_path):
                """从文件路径导入模块"""
                spec = importlib.util.spec_from_file_location(module_name, file_path)
                if spec is None:
                    return None
                module = importlib.util.module_from_spec(spec)
                sys.modules[module_name] = module
                spec.loader.exec_module(module)
                return module
                
            # 导入NER工具模块
            ner_utils_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "utils/ner_utils.py")
            ner_utils = import_module_from_path("ner_utils", ner_utils_path)
            
            # 从模块中读取白名单
            whitelist = list(ner_utils.WHITELIST)
            
            # 创建配置
            config_data = {
                "whitelist": sorted(whitelist)
            }
            
            # 保存到配置文件
            with open(WHITELIST_CONFIG, 'w', encoding='utf-8') as f:
                json.dump(config_data, f, ensure_ascii=False, indent=4)
                
        except Exception as e:
            # #logger.info(f"创建白名单配置文件失败: {str(e)}")
            # 创建默认空白名单
            config_data = {
                "whitelist": []
            }
            with open(WHITELIST_CONFIG, 'w', encoding='utf-8') as f:
                json.dump(config_data, f, ensure_ascii=False, indent=4)

# 初始化配置
ensure_whitelist_config()

# 读取白名单配置
def get_whitelist_config():
    """读取白名单配置"""
    try:
        with open(WHITELIST_CONFIG, 'r', encoding='utf-8') as f:
            return json.load(f)
    except Exception as e:
        # #logger.info(f"读取白名单配置失败: {str(e)}")
        return {
            "whitelist": []
        }

# 全局样式定义 - 使用现代化设计风格
STYLE_SHEET = """
QMainWindow {
    background-color: #f7f8fc;
}

QSplitter::handle {
    background-color: #e8ecf4;
    margin: 1px;
}

QSplitter::handle:horizontal {
    width: 2px;
}

QWidget#leftPanel {
    background-color: #ffffff;
    border-radius: 12px;
    border: 1px solid #e8ecf4;
}

QWidget#rightPanel {
    background-color: #ffffff;
    border-radius: 12px;
    border: 1px solid #e8ecf4;
}

QGroupBox {
    font-weight: bold;
    border: 1px solid #e8ecf4;
    border-radius: 10px;
    margin-top: 16px;
    padding-top: 16px;
    background-color: #fafbfd;
}

QGroupBox::title {
    subcontrol-origin: margin;
    subcontrol-position: top center;
    padding: 0 12px;
    color: #2c3e50;
    background-color: #fafbfd;
}

QPushButton {
    background-color: #1a73e8;
    color: white;
    border: none;
    border-radius: 8px;
    padding: 8px 16px;
    font-weight: bold;
    min-height: 30px;
}

QPushButton:hover {
    background-color: #1557b0;
}

QPushButton:pressed {
    background-color: #1042a0;
}

QPushButton:disabled {
    background-color: #d1d5db;
    color: #9ca3af;
}

QLabel {
    color: #374151;
}

QLineEdit {
    padding: 8px;
    border: 1px solid #e5e7eb;
    border-radius: 8px;
    background-color: #ffffff;
    selection-background-color: #1a73e8;
}

QLineEdit:focus {
    border: 2px solid #1a73e8;
    background-color: #fafbfd;
}

QComboBox {
    padding: 8px;
    border: 1px solid #e5e7eb;
    border-radius: 8px;
    background-color: #ffffff;
    min-height: 30px;
    selection-background-color: #1a73e8;
}

QComboBox:focus {
    border: 2px solid #1a73e8;
    background-color: #fafbfd;
}

QComboBox::drop-down {
    border: none;
    width: 20px;
    border-radius: 0 8px 8px 0;
}

QComboBox::down-arrow {
    image: none;
    border-left: 5px solid transparent;
    border-right: 5px solid transparent;
    border-top: 5px solid #6b7280;
}

QRadioButton {
    spacing: 8px;
    color: #374151;
}

QRadioButton::indicator {
    width: 16px;
    height: 16px;
}

QRadioButton::indicator:unchecked {
    border: 2px solid #d1d5db;
    border-radius: 8px;
    background-color: #ffffff;
}

QRadioButton::indicator:checked {
    border: 2px solid #1a73e8;
    border-radius: 8px;
    background-color: #1a73e8;
}

QProgressBar {
    border: none;
    border-radius: 6px;
    text-align: center;
    background-color: #f3f4f6;
    min-height: 10px;
}

QProgressBar::chunk {
    background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #1a73e8, stop:1 #4285f4);
    border-radius: 6px;
}

QTextEdit {
    border: 1px solid #e5e7eb;
    border-radius: 8px;
    background-color: #ffffff;
    padding: 8px;
    font-family: "Consolas", monospace;
    selection-background-color: #1a73e8;
}

QTextEdit:focus {
    border: 2px solid #1a73e8;
    background-color: #fafbfd;
}

QScrollArea {
    border: none;
    background-color: transparent;
}

QScrollBar:vertical {
    border: none;
    background: #f3f4f6;
    width: 8px;
    border-radius: 4px;
}

QScrollBar::handle:vertical {
    background: #d1d5db;
    min-height: 20px;
    border-radius: 4px;
}

QScrollBar::handle:vertical:hover {
    background: #9ca3af;
}

QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {
    border: none;
    background: none;
}

#startButton {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #10b981, stop:1 #059669);
    color: white;
}

#startButton:hover {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #059669, stop:1 #047857);
}

#startButton:pressed {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #047857, stop:1 #065f46);
}

#cancelButton {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #ef4444, stop:1 #dc2626);
    color: white;
}

#cancelButton:hover {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #dc2626, stop:1 #b91c1c);
}

#cancelButton:pressed {
    background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #b91c1c, stop:1 #991b1b);
}

#appTitleLabel {
    color: #1a73e8;
    font-weight: bold;
}

#logTitleLabel {
    color: #1a73e8;
    font-weight: bold;
}

#logPanel {
    background-color: #ffffff;
    border-radius: 12px;
    border: 1px solid #e8ecf4;
}
"""
class PDFProcessThread(QThread):
    """处理PDF的线程，防止UI卡顿"""
    update_progress = pyqtSignal(int, str)  # 进度, 日志
    finished = pyqtSignal(bool, str)  # 成功/失败, 信息

    def __init__(self, input_path, output_dir, method, use_llm, mask_color=None, mosaic_size=None, replace_char=None, use_combined=False):
        super().__init__()
        self.input_path = input_path
        self.output_dir = output_dir
        self.method = method
        self.use_llm = use_llm
        self.use_combined = use_combined  # 新增：是否使用结合模式
        self.mask_color = mask_color
        self.mosaic_size = mosaic_size
        self.replace_char = replace_char
        self.processed_files = 0  # 已处理文件计数
        
    def run(self):
        try:
            # 使用子进程调用后端脚本
            if self.use_combined:
                script = "main_combined.py"  # 使用结合识别方法的脚本
            elif self.use_llm:
                script = "main_llm.py"  # 使用LLM的脚本
            else:
                script = "main.py"  # 使用NER+正则的脚本
            
            # 构建命令
            cmd = [
                sys.executable,  # 当前Python解释器路径
                script,
                "--input_glob", self.input_path,
                "--out", self.output_dir,
                "--method", self.method
            ]
            
            # 如果是mask方法且指定了颜色，则添加颜色参数
            if self.method == "mask" and self.mask_color:
                cmd.extend(["--mask_color", self.mask_color])
            
            # 如果是blur方法且指定了马赛克块大小，则添加马赛克块大小参数
            if self.method == "blur" and self.mosaic_size:
                cmd.extend(["--mosaic_size", str(self.mosaic_size)])
            
            # 如果是replace方法且指定了替换字符，则添加替换字符参数
            if self.method == "replace" and self.replace_char:
                cmd.extend(["--replace_char", self.replace_char])
            
            # 启动进程并设置回调
            process = subprocess.Popen(
                cmd, 
                stdout=subprocess.PIPE, 
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1,
                universal_newlines=True
            )
            
            # 获取总文件数
            total_files = len(glob.glob(self.input_path))
            self.update_progress.emit(0, f"找到 {total_files} 个PDF文件...")
            
            # 进度追踪变量
            current_file = ""
            
            for line in process.stdout:
                line = line.strip()
                self.update_progress.emit(-1, line)  # 不更新进度，只添加日志
                
                # 检测开始处理新文件的标志
                if "开始处理PDF文件:" in line:
                    current_file = line.split(":", 1)[1].strip()
                
                # 检测单个文件处理完成的标志
                if "PDF处理完成，保存到:" in line and current_file:
                    self.processed_files += 1
                    progress = int((self.processed_files * 100) / total_files) if total_files > 0 else 0
                    self.update_progress.emit(progress, f"已处理 {self.processed_files}/{total_files} 个文件 ({progress}%)")
                
                # 检测全部处理完成的标志
                if "处理完成！成功处理" in line and "/"+str(total_files) in line:
                    self.update_progress.emit(100, "处理完成！")
            
            # 等待进程结束
            process.wait()
            
            # 判断结果
            if process.returncode == 0:
                self.update_progress.emit(100, "处理完成！")
                self.finished.emit(True, f"处理完成！成功处理 {self.processed_files} 个文件。")
            else:
                self.finished.emit(False, "处理失败，请查看日志了解详情。")
                
        except Exception as e:
            self.finished.emit(False, f"发生错误: {str(e)}")


class WhitelistDialog(QDialog):
    """白名单管理对话框"""
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle("白名单管理")
        self.resize(800, 600)  # 调整为更大的尺寸
        self.setup_ui()
        self.load_whitelist_data()
        self.load_ner_settings()
        self.center() # 居中显示
        
    def center(self):
        """将窗口居中显示，确保在垂直和水平方向都居中"""
        # 使用QScreen方式获取屏幕几何信息
        screen = QApplication.primaryScreen().geometry()
        size = self.geometry()
        
        # 计算水平和垂直居中的位置
        x = (screen.width() - size.width()) // 2
        y = (screen.height() - size.height()) // 2
        
        # 移动窗口
        self.move(x, y)
    
    def setup_ui(self):
        """设置界面"""
        layout = QVBoxLayout(self)
        layout.setContentsMargins(20, 20, 20, 20)  # 增加边距
        layout.setSpacing(15)  # 增加间距
        
        # 创建选项卡控件
        self.tab_widget = QTabWidget()
        self.tab_widget.setMinimumHeight(500)  # 设置最小高度
        
        # 地名白名单选项卡
        self.location_tab = QWidget()
        location_layout = QVBoxLayout(self.location_tab)
        location_layout.setContentsMargins(15, 15, 15, 15)  # 增加内边距
        
        location_label = QLabel("通用白名单")
        location_label.setFont(QFont("", 12, QFont.Bold))
        location_layout.addWidget(location_label)
        
        location_desc = QLabel("白名单中的内容将不会被识别为敏感信息，例如'中国'、'北京'等地名，以及'年'、'月'、'日'等时间单位。")
        location_desc.setWordWrap(True)
        location_layout.addWidget(location_desc)
        
        self.location_list = QListWidget()
        self.location_list.setMinimumWidth(300)  # 设置最小宽度
        location_layout.addWidget(self.location_list)
        
        location_buttons = QHBoxLayout()
        self.add_location_btn = QPushButton("添加")
        self.add_location_btn.setMinimumWidth(120)  # 设置按钮最小宽度
        self.add_location_btn.clicked.connect(self.add_location)
        self.delete_location_btn = QPushButton("删除")
        self.delete_location_btn.setMinimumWidth(120)  # 设置按钮最小宽度
        self.delete_location_btn.clicked.connect(self.delete_location)
        location_buttons.addWidget(self.add_location_btn)
        location_buttons.addWidget(self.delete_location_btn)
        location_buttons.addStretch()
        location_layout.addLayout(location_buttons)
        
        # NER敏感类型选项卡
        self.ner_tab = QWidget()
        ner_layout = QVBoxLayout(self.ner_tab)
        
        ner_label = QLabel("NER敏感实体类型设置")
        ner_label.setFont(QFont("", 12, QFont.Bold))
        ner_layout.addWidget(ner_label)
        
        ner_desc = QLabel("勾选需要脱敏的实体类型，未勾选的实体类型将不会被识别为敏感信息。")
        ner_desc.setWordWrap(True)
        ner_layout.addWidget(ner_desc)
        
        # 创建滚动区域容纳复选框
        scroll = QScrollArea()
        scroll.setWidgetResizable(True)
        scroll.setFrameShape(QFrame.NoFrame)
        
        ner_content = QWidget()
        self.ner_content_layout = QVBoxLayout(ner_content)
        
        # 实体类型说明表格
        entity_types = [
            ("CARDINAL", "基数", "例如：一百"),
            ("DATE", "日期", "例如：2025年7月20日"),
            ("EVENT", "事件", "例如：奥运会"),
            ("FAC", "设施", "例如：清华大学"),
            ("GPE", "地缘政治实体", "例如：中国、北京"),
            ("LANGUAGE", "语言", "例如：英语、汉语"),
            ("LAW", "法律", "例如：中华人民共和国宪法"),
            ("LOC", "地点", "例如：长城、泰山"),
            ("MONEY", "金额", "例如：¥100、100美元"),
            ("NORP", "民族/宗教/政党", "例如：中国人、天主教"),
            ("ORDINAL", "序数", "例如：第一、第100"),
            ("ORG", "组织", "例如：百度公司、联合国"),
            ("PERCENT", "百分比", "例如：50%"),
            ("PERSON", "人名", "例如：张三、Alice"),
            ("PRODUCT", "产品", "例如：iPhone 14、寒武纪CPU"),
            ("QUANTITY", "数量", "例如：5公斤、3升"),
            ("TIME", "时间", "例如：下午三点"),
            ("WORK_OF_ART", "艺术作品", "例如：五四运动诗集、指环王")
        ]
        
        # 创建复选框
        self.entity_checkboxes = {}
        for entity_type, name, example in entity_types:
            checkbox = QCheckBox(f"{name} ({entity_type}): {example}")
            self.entity_checkboxes[entity_type] = checkbox
            self.ner_content_layout.addWidget(checkbox)
        
        # 添加全选/全不选按钮
        select_buttons = QHBoxLayout()
        self.select_all_btn = QPushButton("全选")
        self.select_all_btn.clicked.connect(self.select_all_entities)
        self.deselect_all_btn = QPushButton("全不选")
        self.deselect_all_btn.clicked.connect(self.deselect_all_entities)
        select_buttons.addWidget(self.select_all_btn)
        select_buttons.addWidget(self.deselect_all_btn)
        select_buttons.addStretch()
        self.ner_content_layout.addLayout(select_buttons)
        
        scroll.setWidget(ner_content)
        ner_layout.addWidget(scroll)
        
        # 添加选项卡
        self.tab_widget.addTab(self.location_tab, "通用白名单")
        self.tab_widget.addTab(self.ner_tab, "敏感实体类型")
        layout.addWidget(self.tab_widget)
        
        # 底部按钮
        buttons_layout = QHBoxLayout()
        self.save_btn = QPushButton("保存")
        self.save_btn.clicked.connect(self.save_whitelist)
        self.cancel_btn = QPushButton("取消")
        self.cancel_btn.clicked.connect(self.reject)
        buttons_layout.addStretch()
        buttons_layout.addWidget(self.save_btn)
        buttons_layout.addWidget(self.cancel_btn)
        layout.addLayout(buttons_layout)
    
    def load_ner_settings(self):
        """加载NER敏感类型设置"""
        try:
            # 强制重新加载配置模块，避免使用缓存
            config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.py")
            
            # 读取配置文件内容并提取NER_SENSITIVE_TYPES
            with open(config_path, 'r', encoding='utf-8') as f:
                config_content = f.read()
            
            # 从文件内容中提取NER_SENSITIVE_TYPES配置
            ner_sensitive_types = {}
            
            try:
                # 使用importlib强制重新加载模块
                import importlib.util
                import sys
                
                spec = importlib.util.spec_from_file_location("config", config_path)
                config_module = importlib.util.module_from_spec(spec)
                sys.modules["config"] = config_module
                spec.loader.exec_module(config_module)
                
                if hasattr(config_module, 'NER_SENSITIVE_TYPES'):
                    ner_sensitive_types = config_module.NER_SENSITIVE_TYPES
            except Exception as e:
                #logger.info(f"重新加载配置模块失败: {e}")
                # 回退到默认值
                for entity_type in self.entity_checkboxes.keys():
                    ner_sensitive_types[entity_type] = True
            
            # 设置复选框状态
            for entity_type, checkbox in self.entity_checkboxes.items():
                if entity_type in ner_sensitive_types:
                    checkbox.setChecked(ner_sensitive_types[entity_type])
                else:
                    checkbox.setChecked(True)  # 默认勾选
                    
        except Exception as e:
            QMessageBox.warning(self, "加载失败", f"加载NER敏感类型设置失败: {str(e)}")
            # 默认全选
            for checkbox in self.entity_checkboxes.values():
                checkbox.setChecked(True)
    
    def select_all_entities(self):
        """全选所有实体类型"""
        for checkbox in self.entity_checkboxes.values():
            checkbox.setChecked(True)
    
    def deselect_all_entities(self):
        """取消选择所有实体类型"""
        for checkbox in self.entity_checkboxes.values():
            checkbox.setChecked(False)
        
    def load_whitelist_data(self):
        """加载白名单数据"""
        try:
            # 读取配置
            config = get_whitelist_config()
            
            # 加载白名单
            self.location_list.clear()
            for item in config.get("whitelist", []):
                self.location_list.addItem(item)
                
        except Exception as e:
            QMessageBox.warning(self, "加载失败", f"加载白名单数据失败: {str(e)}")
            
    def add_location(self):
        """添加白名单项"""
        text, ok = QInputDialog.getText(self, "添加白名单", "请输入白名单项:")
        if ok and text.strip():
            # 检查是否已存在
            items = [self.location_list.item(i).text() for i in range(self.location_list.count())]
            if text not in items:
                self.location_list.addItem(text.strip())
                self.location_list.sortItems()
                
    def delete_location(self):
        """删除白名单项"""
        selected_items = self.location_list.selectedItems()
        if not selected_items:
            QMessageBox.information(self, "提示", "请先选择要删除的项")
            return
            
        for item in selected_items:
            self.location_list.takeItem(self.location_list.row(item))
            
    def save_whitelist(self):
        """保存白名单数据和NER敏感类型设置"""
        try:
            # 收集白名单
            whitelist = []
            for i in range(self.location_list.count()):
                whitelist.append(self.location_list.item(i).text())
                
            # 创建白名单配置
            config_data = {
                "whitelist": sorted(whitelist)
            }
            
            # 保存到配置文件
            with open(WHITELIST_CONFIG, 'w', encoding='utf-8') as f:
                json.dump(config_data, f, ensure_ascii=False, indent=4)
            
            # 收集NER敏感类型设置
            ner_sensitive_types = {}
            for entity_type, checkbox in self.entity_checkboxes.items():
                ner_sensitive_types[entity_type] = checkbox.isChecked()
            
            # 更新config.py中的NER_SENSITIVE_TYPES
            config_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.py")
            with open(config_path, 'r', encoding='utf-8') as f:
                config_content = f.read()
            
            # 检查是否已存在NER_SENSITIVE_TYPES定义
            if "NER_SENSITIVE_TYPES" in config_content:
                # 替换现有定义
                import re
                pattern = r"NER_SENSITIVE_TYPES\s*=\s*\{[^}]*\}"
                
                # 生成Python格式的字典字符串
                ner_config_str = "NER_SENSITIVE_TYPES = {\n"
                for entity_type, is_sensitive in ner_sensitive_types.items():
                    python_bool = "True" if is_sensitive else "False"  # 使用Python布尔值
                    ner_config_str += f"    '{entity_type}': {python_bool},\n"
                ner_config_str += "}"
                
                new_content = re.sub(pattern, ner_config_str, config_content)
            else:
                # 添加新定义
                ner_config_str = "# NER实体类型配置\n"
                ner_config_str += "# 设置为True的实体类型将被识别为敏感信息\n"
                ner_config_str += "NER_SENSITIVE_TYPES = {\n"
                for entity_type, is_sensitive in ner_sensitive_types.items():
                    python_bool = "True" if is_sensitive else "False"  # 使用Python布尔值
                    ner_config_str += f"    '{entity_type}': {python_bool},\n"
                ner_config_str += "}"
                
                new_content = config_content + "\n\n" + ner_config_str
            
            # 写回配置文件
            with open(config_path, 'w', encoding='utf-8') as f:
                f.write(new_content)
            
            # 重新加载白名单
            try:
                ner_utils.reload_whitelist()
                # #logger.info("白名单已重新加载")
            except Exception as e:
                logger.info(f"重新加载白名单失败: {str(e)}")
                
            QMessageBox.information(self, "成功", "设置已保存")
            self.accept()
            
        except Exception as e:
            QMessageBox.critical(self, "保存失败", f"保存设置失败: {str(e)}")

class PDFDesensitizationUI(QMainWindow):
    """PDF脱敏工具的主界面"""
    
    def __init__(self):
        super().__init__()
        # 初始化字体比例计算
        self.initFontScaling()
        self.initUI()
        self.current_mosaic_size = DEFAULT_MOSAIC_SIZE  # 默认马赛克块大小
        self.current_replace_char = DEFAULT_REPLACE_CHAR  # 默认替换字符
        
    def initFontScaling(self):
        """初始化字体缩放系统"""
        # 获取系统默认字体信息
        app = QApplication.instance()
        default_font = app.font()
        font_info = QFontInfo(default_font)
        
        # 计算基准字体大小
        self.base_font_size = font_info.pointSize()
        if self.base_font_size <= 0:  # 某些系统可能返回像素大小
            self.base_font_size = 10  # 设置默认值
            
        # 定义各种UI元素的字体大小比例
        self.font_scales = {
            'title': 2.0,        # 标题比例
            'group_title': 1.3,  # 分组标题
            'label': 1.1,        # 标签
            'button': 1.1,       # 按钮
            'radio': 1.2,        # 单选按钮
            'edit': 1.1,         # 编辑框
            'log': 1.1,          # 日志区
            'footer': 1.1        # 页脚
        }
    
    def getScaledFont(self, element_type, bold=False):
        """根据元素类型获取按比例缩放的字体"""
        font = QFont()
        if element_type in self.font_scales:
            size = int(self.base_font_size * self.font_scales[element_type])
            font.setPointSize(size)
        else:
            font.setPointSize(self.base_font_size)
        
        font.setBold(bold)
        return font
        
    def initUI(self):
        # 设置窗口基本属性
        self.setWindowTitle('PDF文档脱敏工具')
        
        # 设置窗口尺寸为屏幕尺寸的80%，并居中显示
        screen = QDesktopWidget().screenGeometry()
        width = int(screen.width() * 0.8)
        height = int(screen.height() * 0.8)
        self.resize(width, height)
        self.center()
        
        # 设置应用图标
        if os.path.exists(APP_ICON):
            # 如果自定义图标存在，则使用它
            self.setWindowIcon(QIcon(APP_ICON))
        else:
            # 如果自定义图标不存在，则尝试使用系统图标
            app_icon = QIcon.fromTheme("document-encrypt", QIcon.fromTheme("pdf"))
            # 如果系统主题没有提供图标，尝试使用PyQt内置图标
            if app_icon.isNull():
                app_icon = QIcon.fromTheme("application-pdf")
            # 如果还是没有图标，使用标准图标
            if app_icon.isNull():
                app_icon = QIcon.fromTheme("accessories-text-editor")
            self.setWindowIcon(app_icon)
            
            # 提示用户放置自定义图标
            # #logger.info(f"提示: 如需自定义应用图标，请将图标文件保存为: {APP_ICON}")
        
        # 应用全局样式
        self.setStyleSheet(STYLE_SHEET)
        
        # 创建中央Widget
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        # 创建主布局
        main_layout = QHBoxLayout(central_widget)
        main_layout.setContentsMargins(15, 15, 15, 15)
        main_layout.setSpacing(10)
        
        # 创建分割器
        splitter = QSplitter(Qt.Horizontal)
        splitter.setHandleWidth(2)
        
        # ======= 左侧面板 =======
        left_panel = QWidget()
        left_panel.setObjectName("leftPanel")
        left_layout = QVBoxLayout(left_panel)
        left_layout.setContentsMargins(15, 15, 15, 15)
        left_layout.setSpacing(15)
        
        # 标题
        title_label = QLabel("PDF文档脱敏系统")
        title_label.setObjectName("appTitleLabel")
        title_label.setFont(self.getScaledFont('title', bold=True))
        title_label.setAlignment(Qt.AlignCenter)
        left_layout.addWidget(title_label)
        
        # 使用滚动区域包装配置部分
        scroll_area = QScrollArea()
        scroll_area.setWidgetResizable(True)
        scroll_area.setFrameShape(QFrame.NoFrame)
        
        scroll_content = QWidget()
        scroll_layout = QVBoxLayout(scroll_content)
        scroll_layout.setContentsMargins(0, 0, 10, 0)
        scroll_layout.setSpacing(20)
        
        # ======= 文件选择区域 =======
        file_group = QGroupBox("文件选择")
        file_group.setFont(self.getScaledFont('group_title', bold=True))
        file_layout = QVBoxLayout()
        file_layout.setContentsMargins(15, 25, 15, 15)
        file_layout.setSpacing(15)
        
        # 单文件选择
        file_select_layout = QHBoxLayout()
        file_select_layout.setSpacing(10)
        
        file_label = QLabel("文件:")
        file_label.setFont(self.getScaledFont('label'))
        file_label.setMinimumWidth(80)
        
        self.file_path_edit = QLineEdit()
        self.file_path_edit.setPlaceholderText("选择PDF文件或输入通配符路径")
        self.file_path_edit.setReadOnly(True)
        self.file_path_edit.setFont(self.getScaledFont('edit'))
        
        file_select_btn = QPushButton("浏览")
        file_select_btn.setFont(self.getScaledFont('button'))
        file_select_btn.setIcon(QIcon.fromTheme("document-open"))
        file_select_btn.setMaximumWidth(100)
        file_select_btn.clicked.connect(self.select_file)
        
        file_select_layout.addWidget(file_label)
        file_select_layout.addWidget(self.file_path_edit)
        file_select_layout.addWidget(file_select_btn)
        file_layout.addLayout(file_select_layout)
        
        # 文件夹选择
        folder_select_layout = QHBoxLayout()
        folder_select_layout.setSpacing(10)
        
        folder_label = QLabel("文件夹:")
        folder_label.setFont(self.getScaledFont('label'))
        folder_label.setMinimumWidth(80)
        
        self.folder_path_edit = QLineEdit()
        self.folder_path_edit.setPlaceholderText("选择含有PDF文件的文件夹")
        self.folder_path_edit.setReadOnly(True)
        self.folder_path_edit.setFont(self.getScaledFont('edit'))
        
        folder_select_btn = QPushButton("浏览")
        folder_select_btn.setFont(self.getScaledFont('button'))
        folder_select_btn.setIcon(QIcon.fromTheme("folder-open"))
        folder_select_btn.setMaximumWidth(100)
        folder_select_btn.clicked.connect(self.select_folder)
        
        folder_select_layout.addWidget(folder_label)
        folder_select_layout.addWidget(self.folder_path_edit)
        folder_select_layout.addWidget(folder_select_btn)
        file_layout.addLayout(folder_select_layout)
        
        # 输出目录选择
        output_select_layout = QHBoxLayout()
        output_select_layout.setSpacing(10)
        
        output_label = QLabel("输出目录:")
        output_label.setFont(self.getScaledFont('label'))
        output_label.setMinimumWidth(80)
        
        self.output_path_edit = QLineEdit()
        self.output_path_edit.setText("./out")
        self.output_path_edit.setFont(self.getScaledFont('edit'))
        
        output_select_btn = QPushButton("浏览")
        output_select_btn.setFont(self.getScaledFont('button'))
        output_select_btn.setIcon(QIcon.fromTheme("folder"))
        output_select_btn.setMaximumWidth(100)
        output_select_btn.clicked.connect(self.select_output_dir)
        
        output_select_layout.addWidget(output_label)
        output_select_layout.addWidget(self.output_path_edit)
        output_select_layout.addWidget(output_select_btn)
        file_layout.addLayout(output_select_layout)
        
        file_group.setLayout(file_layout)
        scroll_layout.addWidget(file_group)
        
        # ======= 处理参数设置区域 =======
        params_group = QGroupBox("处理参数")
        params_group.setFont(self.getScaledFont('group_title', bold=True))
        params_layout = QVBoxLayout()
        params_layout.setContentsMargins(15, 25, 15, 15)
        params_layout.setSpacing(20)
        
        # 脱敏方法选择
        method_layout = QVBoxLayout()
        method_layout.setSpacing(10)
        
        method_label = QLabel("脱敏方法:")
        method_label.setFont(self.getScaledFont('label'))
        
        self.method_combo = QComboBox()
        self.method_combo.setMinimumHeight(35)
        self.method_combo.setFont(self.getScaledFont('edit'))
        for method in DESENSE_METHODS:
            self.method_combo.addItem(method)
        
        # 连接信号，以便在选择"mask"时显示颜色选择器
        self.method_combo.currentTextChanged.connect(self.on_method_changed)
            
        method_layout.addWidget(method_label)
        method_layout.addWidget(self.method_combo)
        params_layout.addLayout(method_layout)
        
        # 创建颜色选择区域
        self.color_layout = QVBoxLayout()
        self.color_layout.setSpacing(10)
        
        color_label = QLabel("遮罩颜色:")
        color_label.setFont(self.getScaledFont('label'))
        self.color_layout.addWidget(color_label)
        
        # 颜色预设按钮
        presets_layout = QHBoxLayout()
        presets_layout.setSpacing(5)
        
        # 从配置中添加预设颜色按钮
        self.color_buttons = []
        for preset in MASK_COLORS_PRESETS:
            color = preset['color']  # 捕获当前循环中的颜色值
            btn = QPushButton("")
            btn.setStyleSheet(f"background-color: {color}; min-width: 30px; min-height: 30px; border-radius: 15px;")
            btn.setToolTip(preset['name'])
            # 创建新函数来避免闭包问题
            def create_color_handler(color_value):
                return lambda checked: self.set_mask_color(color_value)
            btn.clicked.connect(create_color_handler(color))
            presets_layout.addWidget(btn)
            self.color_buttons.append(btn)
        
        presets_layout.addStretch()
        self.color_layout.addLayout(presets_layout)
        
        # 自定义颜色按钮和显示
        color_select_layout = QHBoxLayout()
        
        self.current_color_display = QFrame()
        self.current_color_display.setMinimumSize(30, 30)
        self.current_color_display.setMaximumSize(30, 30)
        self.current_color_display.setStyleSheet(f"background-color: {MASK_COLOR}; border-radius: 15px; border: 1px solid #ccc;")
        
        self.color_value_label = QLabel(MASK_COLOR)
        self.color_value_label.setMinimumWidth(70)
        
        self.custom_color_btn = QPushButton("自定义颜色")
        self.custom_color_btn.clicked.connect(self.select_custom_color)
        
        color_select_layout.addWidget(self.current_color_display)
        color_select_layout.addWidget(self.color_value_label)
        color_select_layout.addWidget(self.custom_color_btn)
        color_select_layout.addStretch()
        
        self.color_layout.addLayout(color_select_layout)
        
        # 创建马赛克块大小选择区域
        self.mosaic_layout = QVBoxLayout()
        self.mosaic_layout.setSpacing(10)
        
        mosaic_label = QLabel("马赛克块大小:")
        mosaic_label.setFont(self.getScaledFont('label'))
        self.mosaic_layout.addWidget(mosaic_label)
        
        # 预设马赛克大小按钮
        mosaic_presets_layout = QHBoxLayout()
        
        # 添加预设大小按钮
        mosaic_sizes = [5, 10, 15, 20, 30]
        self.mosaic_buttons = []
        
        for size in mosaic_sizes:
            btn = QPushButton(str(size))
            btn.setMinimumWidth(40)
            btn.setMinimumHeight(30)
            btn.setToolTip(f"{size}x{size} 像素")
            # 创建新函数来避免闭包问题
            def create_size_handler(size_value):
                return lambda checked: self.set_mosaic_size(size_value)
            btn.clicked.connect(create_size_handler(size))
            mosaic_presets_layout.addWidget(btn)
            self.mosaic_buttons.append(btn)
            
        mosaic_presets_layout.addStretch()
        self.mosaic_layout.addLayout(mosaic_presets_layout)
        
        # 自定义马赛克块大小
        mosaic_custom_layout = QHBoxLayout()
        
        self.mosaic_size_label = QLabel(str(DEFAULT_MOSAIC_SIZE))
        self.mosaic_size_label.setMinimumWidth(30)
        
        self.mosaic_size_slider = QSlider(Qt.Horizontal)
        self.mosaic_size_slider.setMinimum(3)
        self.mosaic_size_slider.setMaximum(50)
        self.mosaic_size_slider.setValue(DEFAULT_MOSAIC_SIZE)
        self.mosaic_size_slider.setTickPosition(QSlider.TicksBelow)
        self.mosaic_size_slider.setTickInterval(5)
        self.mosaic_size_slider.valueChanged.connect(self.on_mosaic_slider_changed)
        
        mosaic_custom_layout.addWidget(self.mosaic_size_label)
        mosaic_custom_layout.addWidget(self.mosaic_size_slider)
        
        self.mosaic_layout.addLayout(mosaic_custom_layout)
        
        # 创建替换字符选择区域
        self.replace_layout = QVBoxLayout()
        self.replace_layout.setSpacing(10)
        
        replace_label = QLabel("替换字符:")
        replace_label.setFont(self.getScaledFont('label'))
        self.replace_layout.addWidget(replace_label)
        
        # 预设替换字符按钮
        replace_buttons_layout = QHBoxLayout()
        
        # 添加预设替换字符按钮
        self.replace_buttons = []
        for char in REPLACE_CHAR_PRESETS:
            btn = QPushButton(char)
            btn.setMinimumWidth(40)
            btn.setMinimumHeight(30)
            btn.setToolTip(f"使用 '{char}' 作为替换字符")
            # 创建新函数来避免闭包问题
            def create_char_handler(char_value):
                return lambda checked: self.set_replace_char(char_value)
            btn.clicked.connect(create_char_handler(char))
            replace_buttons_layout.addWidget(btn)
            self.replace_buttons.append(btn)
        
        replace_buttons_layout.addStretch()
        self.replace_layout.addLayout(replace_buttons_layout)
        
        # 自定义替换字符
        replace_custom_layout = QHBoxLayout()
        
        self.replace_char_edit = QLineEdit(DEFAULT_REPLACE_CHAR)
        self.replace_char_edit.setMaxLength(1)
        self.replace_char_edit.setMinimumWidth(60)
        self.replace_char_edit.setFixedWidth(80)
        self.replace_char_edit.setAlignment(Qt.AlignCenter)
        self.replace_char_edit.setFont(QFont("", 14, QFont.Bold))
        self.replace_char_edit.textChanged.connect(self.on_replace_char_changed)
        
        self.custom_char_label = QLabel("自定义字符:")
        
        replace_custom_layout.addWidget(self.custom_char_label)
        replace_custom_layout.addWidget(self.replace_char_edit)
        replace_custom_layout.addStretch()
        
        self.replace_layout.addLayout(replace_custom_layout)
        
        # 将所有选择控件添加到布局
        params_layout.addLayout(self.color_layout)
        params_layout.addLayout(self.mosaic_layout)
        params_layout.addLayout(self.replace_layout)
        
        # 初始根据当前选择的方法显示或隐藏相应控件
        self.on_method_changed(self.method_combo.currentText())
        
        # 敏感词检测方法选择
        detect_method_layout = QVBoxLayout()
        detect_method_layout.setSpacing(10)
        
        detect_method_label = QLabel("敏感词检测方法:")
        detect_method_label.setFont(self.getScaledFont('label'))
        detect_method_layout.addWidget(detect_method_label)
        
        self.detect_method_group = QButtonGroup()
        radio_layout = QHBoxLayout()
        radio_layout.setSpacing(20)
        
        self.ner_radio = QRadioButton("NER+正则表达式")
        self.ner_radio.setChecked(True)
        self.ner_radio.setFont(self.getScaledFont('radio'))
        
        self.llm_radio = QRadioButton("LLM大模型")
        self.llm_radio.setFont(self.getScaledFont('radio'))
        
        self.combined_radio = QRadioButton("结合模式")
        self.combined_radio.setFont(self.getScaledFont('radio'))

        self.detect_method_group.addButton(self.ner_radio, 1)
        self.detect_method_group.addButton(self.llm_radio, 2)
        self.detect_method_group.addButton(self.combined_radio, 3)
        
        radio_layout.addWidget(self.ner_radio)
        radio_layout.addWidget(self.llm_radio)
        radio_layout.addWidget(self.combined_radio)
        radio_layout.addStretch()
        
        detect_method_layout.addLayout(radio_layout)
        params_layout.addLayout(detect_method_layout)
        
        # 添加操作按钮
        buttons_layout = QHBoxLayout()
        buttons_layout.setSpacing(15)
        
        self.start_btn = QPushButton("开始处理")
        self.start_btn.setMinimumHeight(45)
        self.start_btn.setObjectName("startButton")
        self.start_btn.setIcon(QIcon.fromTheme("media-playback-start"))
        self.start_btn.clicked.connect(self.start_processing)
        self.start_btn.setFont(self.getScaledFont('button', bold=True))
        
        self.cancel_btn = QPushButton("取消")
        self.cancel_btn.setMinimumHeight(45)
        self.cancel_btn.setObjectName("cancelButton")
        self.cancel_btn.setIcon(QIcon.fromTheme("process-stop"))
        self.cancel_btn.clicked.connect(self.cancel_processing)
        self.cancel_btn.setEnabled(False)
        self.cancel_btn.setFont(self.getScaledFont('button', bold=True))
        
        buttons_layout.addWidget(self.start_btn)
        buttons_layout.addWidget(self.cancel_btn)
        params_layout.addLayout(buttons_layout)
        
        # 添加白名单管理按钮
        whitelist_btn = QPushButton("白名单管理")
        whitelist_btn.setMinimumHeight(35)
        whitelist_btn.setFont(self.getScaledFont('button'))
        whitelist_btn.clicked.connect(self.open_whitelist_manager)
        params_layout.addWidget(whitelist_btn)
        
        params_group.setLayout(params_layout)
        scroll_layout.addWidget(params_group)
        
        # 版本信息
        version_label = QLabel("v1.0.0  © 2025 PDF脱敏工具")
        version_label.setAlignment(Qt.AlignCenter)
        version_label.setFont(self.getScaledFont('footer'))
        version_label.setStyleSheet("color: #999999; margin-top: 10px;")
        scroll_layout.addWidget(version_label)
        
        # 添加弹性空间
        scroll_layout.addStretch()
        
        scroll_area.setWidget(scroll_content)
        left_layout.addWidget(scroll_area)
        
        # ======= 右侧面板 =======
        right_panel = QWidget()
        right_panel.setObjectName("rightPanel")
        right_layout = QVBoxLayout(right_panel)
        right_layout.setContentsMargins(15, 15, 15, 15)
        right_layout.setSpacing(15)
        
        # 日志标题
        log_title = QLabel("处理日志")
        log_title.setObjectName("logTitleLabel")
        log_title.setFont(self.getScaledFont('group_title', bold=True))
        log_title.setAlignment(Qt.AlignLeft)
        right_layout.addWidget(log_title)
        
        # 日志面板
        log_panel = QWidget()
        log_panel.setObjectName("logPanel")
        log_panel_layout = QVBoxLayout(log_panel)
        log_panel_layout.setContentsMargins(15, 15, 15, 15)
        log_panel_layout.setSpacing(10)
        
        # 日志显示区域
        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        log_font = QFont("Consolas")
        log_font.setPointSize(int(self.base_font_size * self.font_scales['log']))
        self.log_text.setFont(log_font)
        log_panel_layout.addWidget(self.log_text)
        
        # 日志控制按钮区域
        log_ctrl_layout = QHBoxLayout()
        
        # 清空日志按钮
        self.clear_log_btn = QPushButton("清空日志")
        self.clear_log_btn.clicked.connect(self.clear_log)
        
        # 导出日志按钮
        self.export_log_btn = QPushButton("导出日志")
        self.export_log_btn.clicked.connect(self.export_log)
        
        # 添加按钮到布局
        log_ctrl_layout.addWidget(self.clear_log_btn)
        log_ctrl_layout.addWidget(self.export_log_btn)
        log_ctrl_layout.addStretch()
        
        log_panel_layout.addLayout(log_ctrl_layout)
        
        # 进度条
        progress_layout = QVBoxLayout()
        progress_layout.setSpacing(5)
        
        progress_label = QLabel("处理进度:")
        progress_label.setFont(self.getScaledFont('label'))
        progress_layout.addWidget(progress_label)
        
        self.progress_bar = QProgressBar()
        self.progress_bar.setMinimumHeight(25)
        self.progress_bar.setTextVisible(True)
        self.progress_bar.setFormat("%p%")
        progress_layout.addWidget(self.progress_bar)
        
        log_panel_layout.addLayout(progress_layout)
        right_layout.addWidget(log_panel)
        
        # 添加面板到分割器
        splitter.addWidget(left_panel)
        splitter.addWidget(right_panel)
        
        # 设置分割器初始大小
        splitter.setSizes([int(width * 0.4), int(width * 0.6)])
        
        # 添加分割器到主布局
        main_layout.addWidget(splitter)
        
    def select_file(self):
        """选择单个PDF文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择PDF文件", "", "PDF Files (*.pdf);;All Files (*)"
        )
        if file_path:
            self.file_path_edit.setText(file_path)
            self.folder_path_edit.clear()  # 清除文件夹选择
    
    def select_folder(self):
        """选择包含PDF的文件夹"""
        folder_path = QFileDialog.getExistingDirectory(self, "选择文件夹")
        if folder_path:
            self.folder_path_edit.setText(f"{folder_path}/*.pdf")
            self.file_path_edit.clear()  # 清除文件选择
    
    def select_output_dir(self):
        """选择输出目录"""
        folder_path = QFileDialog.getExistingDirectory(self, "选择输出目录")
        if folder_path:
            self.output_path_edit.setText(folder_path)
    
    def on_method_changed(self, method):
        """处理脱敏方法变化的槽函数"""
        # 显示/隐藏相应控件
        is_mask = (method == "mask")
        is_blur = (method == "blur")
        is_replace = (method == "replace")
        
        # 控制颜色选择器的可见性
        for i in range(self.color_layout.count()):
            item = self.color_layout.itemAt(i)
            if item:
                widget = item.widget()
                if widget:
                    widget.setVisible(is_mask)
                else:
                    layout = item.layout()
                    if layout:
                        for j in range(layout.count()):
                            if layout.itemAt(j) and layout.itemAt(j).widget():
                                layout.itemAt(j).widget().setVisible(is_mask)
                                
        # 控制马赛克块大小选择器的可见性
        for i in range(self.mosaic_layout.count()):
            item = self.mosaic_layout.itemAt(i)
            if item:
                widget = item.widget()
                if widget:
                    widget.setVisible(is_blur)
                else:
                    layout = item.layout()
                    if layout:
                        for j in range(layout.count()):
                            if layout.itemAt(j) and layout.itemAt(j).widget():
                                layout.itemAt(j).widget().setVisible(is_blur)
                                
        # 控制替换字符选择器的可见性
        for i in range(self.replace_layout.count()):
            item = self.replace_layout.itemAt(i)
            if item:
                widget = item.widget()
                if widget:
                    widget.setVisible(is_replace)
                else:
                    layout = item.layout()
                    if layout:
                        for j in range(layout.count()):
                            if layout.itemAt(j) and layout.itemAt(j).widget():
                                layout.itemAt(j).widget().setVisible(is_replace)
    
    def set_mask_color(self, color):
        """设置遮罩颜色"""
        self.current_color_display.setStyleSheet(f"background-color: {color}; border-radius: 15px; border: 1px solid #ccc;")
        self.color_value_label.setText(color)
    
    def select_custom_color(self):
        """打开颜色选择对话框"""
        color = QColorDialog.getColor(QColor(self.color_value_label.text()), self, "选择遮罩颜色")
        if color.isValid():
            color_hex = color.name()
            self.set_mask_color(color_hex)
    
    def set_mosaic_size(self, size):
        """设置马赛克块大小"""
        self.current_mosaic_size = size
        self.mosaic_size_label.setText(str(size))
        self.mosaic_size_slider.setValue(size)
    
    def on_mosaic_slider_changed(self, value):
        """处理滑块值变化"""
        self.current_mosaic_size = value
        self.mosaic_size_label.setText(str(value))
        
    def set_replace_char(self, char):
        """设置替换字符"""
        if char and len(char) > 0:
            self.current_replace_char = char[0]  # 只取第一个字符
            self.replace_char_edit.setText(self.current_replace_char)
    
    def on_replace_char_changed(self, text):
        """处理替换字符输入框变化"""
        if text:
            self.current_replace_char = text[0]  # 只取第一个字符
        else:
            self.current_replace_char = DEFAULT_REPLACE_CHAR
    
    def start_processing(self):
        """开始处理PDF文件"""
        # 获取输入路径
        input_path = self.file_path_edit.text() or self.folder_path_edit.text()
        if not input_path:
            QMessageBox.warning(self, "警告", "请先选择PDF文件或文件夹！")
            return
        
        # 获取输出目录
        output_dir = self.output_path_edit.text()
        if not output_dir:
            QMessageBox.warning(self, "警告", "请设置输出目录！")
            return
        
        # 确保输出目录存在
        os.makedirs(output_dir, exist_ok=True)
        
        # 获取处理参数
        method = self.method_combo.currentText()
        use_llm = self.llm_radio.isChecked()
        use_combined = self.combined_radio.isChecked()  # 新增：是否使用结合模式
        
        # 获取mask颜色、blur块大小或replace字符
        mask_color = None
        mosaic_size = None
        replace_char = None
        
        if method == "mask":
            mask_color = self.color_value_label.text()
        elif method == "blur":
            mosaic_size = self.current_mosaic_size
        elif method == "replace":
            replace_char = self.current_replace_char
        
        # 清空日志并初始化进度条
        self.log_text.clear()
        self.progress_bar.setValue(0)
        
        # 更新UI状态
        self.start_btn.setEnabled(False)
        self.cancel_btn.setEnabled(True)
        
        # 开始处理线程
        self.process_thread = PDFProcessThread(
            input_path, 
            output_dir, 
            method, 
            use_llm,
            mask_color, 
            mosaic_size, 
            replace_char,
            use_combined  # 新增：传递是否使用结合模式
        )
        self.process_thread.update_progress.connect(self.update_progress)
        self.process_thread.finished.connect(self.process_finished)
        self.process_thread.start()
        
        # 添加初始日志
        self.log_text.append("开始处理PDF文件...")
        self.log_text.append(f"输入: {input_path}")
        self.log_text.append(f"输出目录: {output_dir}")
        self.log_text.append(f"脱敏方法: {method}")
        if method == "mask" and mask_color:
            self.log_text.append(f"遮罩颜色: {mask_color}")
        elif method == "blur" and mosaic_size:
            self.log_text.append(f"马赛克块大小: {mosaic_size}x{mosaic_size} 像素")
        elif method == "replace" and replace_char:
            self.log_text.append(f"替换字符: '{replace_char}'")
        # 显示当前使用的识别方法
        if use_combined:
            self.log_text.append("敏感词检测方法: 结合模式(正则+NER+LLM)")
        elif use_llm:
            self.log_text.append("敏感词检测方法: LLM大模型")
        else:
            self.log_text.append("敏感词检测方法: NER+正则")
        self.log_text.append("=" * 40)
    
    def cancel_processing(self):
        """取消处理"""
        if self.process_thread and self.process_thread.isRunning():
            self.process_thread.terminate()
            self.process_thread.wait()
            self.process_finished(False, "处理已取消")
    
    def update_progress(self, progress, message):
        """更新进度条和日志"""
        if progress >= 0:  # 只有当提供了有效的进度值时才更新进度条
            self.progress_bar.setValue(progress)
        
        if message:
            self.log_text.append(message)
            # 自动滚动到最新日志
            self.log_text.ensureCursorVisible()
    
    def process_finished(self, success, message):
        """处理完成回调"""
        # 恢复UI状态
        self.start_btn.setEnabled(True)
        self.cancel_btn.setEnabled(False)
        
        # 显示处理结果
        if success:
            self.progress_bar.setValue(100)
            self.log_text.append("\n" + "=" * 20)
            self.log_text.append(message)
            QMessageBox.information(self, "处理完成", message)
        else:
            self.log_text.append("\n" + "=" * 20)
            self.log_text.append(f"错误: {message}")
            QMessageBox.warning(self, "处理出错", message)

    def center(self):
        """将窗口居中显示"""
        qr = self.frameGeometry()
        cp = QDesktopWidget().availableGeometry().center()
        qr.moveCenter(cp)
        self.move(qr.topLeft())

    def open_whitelist_manager(self):
        """打开白名单管理对话框"""
        dialog = WhitelistDialog(self)
        dialog.center() # 确保居中显示
        if dialog.exec_() == QDialog.Accepted:
            # 白名单已更新，可以在此处进行其他操作
            pass

    def clear_log(self):
        """清空日志区域"""
        self.log_text.clear()
        
    def export_log(self):
        """导出日志到本地文件"""
        # 获取当前时间作为默认文件名
        import datetime
        now = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
        default_filename = f"pdf_desense_log_{now}.txt"
        
        # 打开文件保存对话框
        file_path, _ = QFileDialog.getSaveFileName(
            self,
            "导出日志",
            default_filename,
            "文本文件 (*.txt);;所有文件 (*)"
        )
        
        if file_path:
            try:
                # 获取日志内容
                log_content = self.log_text.toPlainText()
                
                # 写入文件
                with open(file_path, 'w', encoding='utf-8') as f:
                    f.write(log_content)
                    
                QMessageBox.information(self, "导出成功", f"日志已成功导出到: {file_path}")
            except Exception as e:
                QMessageBox.critical(self, "导出失败", f"导出日志时发生错误: {str(e)}")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    
    # 启用高DPI缩放
    app.setAttribute(Qt.AA_EnableHighDpiScaling, True)
    app.setAttribute(Qt.AA_UseHighDpiPixmaps, True)
    
    # 使用系统字体，设置合适的默认大小
    default_font = QFont()
    # Windows系统上默认使用微软雅黑
    if sys.platform == 'win32':
        default_font = QFont("Microsoft YaHei")
    # macOS系统使用系统默认字体
    elif sys.platform == 'darwin':
        default_font = QFont(".AppleSystemUIFont")
    # Linux系统使用系统默认字体
    else:
        default_font = QFont("Sans Serif")
    
    app.setFont(default_font)
    
    window = PDFDesensitizationUI()
    window.show()
    sys.exit(app.exec_()) 